package com.pro.framework.lock.redisson;

import lombok.AllArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

@Component
@AllArgsConstructor
@Slf4j
public class RedissonLockExecutor {

    private RedissonClient redissonClient;

    @SneakyThrows
    public <T> T executeWithLock(String lockKey, long timeoutSeconds, Supplier<T> task, T failResult) {
        RLock lock = redissonClient.getLock(lockKey);
        boolean locked = false;
        try {
            locked = lock.tryLock(5, timeoutSeconds, TimeUnit.SECONDS);
            if (!locked) {
                log.warn("任务已在执行，跳过本次请求，lockKey={}", lockKey);
                return failResult;
            }
            return task.get();
        } catch (Exception e) {
            if (e.getClass().getSimpleName().equals("RetryableException")) {
                log.warn("执行异常 {} {}", lockKey, e.getMessage());
            } else {
                log.error("执行异常 {} {}", lockKey, e.getMessage(), e);
            }
            throw e;
        } finally {
            // 只在当前线程成功加锁后再释放锁
            if (locked && lock.isHeldByCurrentThread()) {
                try {
                    lock.unlock();
                } catch (Exception e) {
                    log.error("释放锁失败，lockKey={}", lockKey, e);
                }
            }
        }
    }

    @SneakyThrows
    public <T> T executeWithLocks(List<String> lockKeys, long timeoutSeconds, Supplier<T> task, Supplier<T> failResult) {
        List<RLock> locks = lockKeys.stream()
                .sorted() // 保证顺序，避免死锁
                .map(redissonClient::getLock)
                .toList();

        List<RLock> acquiredLocks = new ArrayList<>();
        try {
            for (RLock lock : locks) {
                boolean locked = lock.tryLock(5, timeoutSeconds, TimeUnit.SECONDS);
                if (!locked) {
                    log.warn("任务已在执行，跳过本次请求，lockKeys={}", lockKeys);
                    return failResult.get();
                }
                acquiredLocks.add(lock);
            }
            return task.get();
        } finally {
            // 释放锁，倒序释放
            Collections.reverse(acquiredLocks);
            for (RLock lock : acquiredLocks) {
                if (lock.isHeldByCurrentThread()) {
                    try {
                        lock.unlock();
                    } catch (Exception e) {
                        log.error("释放锁失败，lockKey={}", lock.getName(), e);
                    }
                }
            }
        }
    }

}
